﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ComponentModel;


namespace KrmSoftware
{
    public class ClassExtender
    {
        #region Private Methods
        private String[] attrs = null;        
        #endregion

        #region Constructor
        public ClassExtender()
        {
            ClassObject = this;
        }

        public ClassExtender(object classObject)
        {
            this.ClassObject = classObject;
        }
        #endregion

        public object ClassObject { get; set; }

        #region Public Methods
        /// <summary>
        /// Method: Returns Correct Case of Class Property
        /// </summary>
        /// <param name="propertyName">The name of the property to return correct case for.</param>
        /// <returns>string of property in the correct case.</returns>
        private string ClassProperty(string propertyName)
        {
            return (from c in ClassProperties()
                    where c.ToLower() == propertyName.ToLower()
                    select c).SingleOrDefault().ToString();

        }

        /// <summary>
        /// Method that returns string[] of class properties
        /// </summary>
        /// <returns>string[] of class properties</returns>
        public String[] ClassProperties()
        {
            if (attrs != null)
                return attrs;

            try
            {
                int propCount = ClassObject.GetType().GetProperties().Count();
                attrs = new String[propCount];
                int i = 0;

                foreach (PropertyInfo pi in ClassObject.GetType().GetProperties())
                {
                    attrs[i] = pi.Name;
                    i++;
                }
                return attrs;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Method : Returns PropertyInfo based on propertyName
        /// </summary>
        /// <param name="propertyName">Name of Property on Class - CASE INSENSITIVE</param>
        /// <returns>PropertyInfo object of Class.</returns>
        public PropertyInfo ClassPropertyInfo(string propertyName)
        {
            PropertyInfo pi = null;
            try
            {
                string s = ClassProperty(propertyName);
                pi = ClassObject.GetType().GetProperty(s);
                return pi;
            }
            catch
            {
                //Can't find the property - just Return;
                return pi;
            }

        }

        /// <summary>
        /// Method: Sets the value for the specified propertyname
        /// </summary>
        /// <param name="propertyName">Name of Property on Class - CASE INSENSITIVE</param>
        /// <param name="value">System.String: The value</param>
        public void SetValue(string propertyName, string value)
        {
            try
            {
                PropertyInfo pi = ClassPropertyInfo(propertyName);

                //TODO: Make this dynamic
                if (pi == null)
                    return;

                if (pi.PropertyType == typeof(System.DateTime))
                {
                    pi.SetValue(ClassObject, ConvertDateString(value), null);
                    return;
                }

                if (pi.PropertyType == typeof(System.String))
                {
                    pi.SetValue(ClassObject, value.Replace('$', ' '), null);
                    return;
                }

                if (pi.PropertyType == typeof(int))
                {
                    pi.SetValue(ClassObject, Convert.ToInt32(value), null);
                    return;
                }

                if (pi.PropertyType == typeof(string[]))
                {
                    pi.SetValue(ClassObject, value, null);
                }

                //TODO: FIX THIS.. IT SHOULD NEVER GET HERE.
                pi.SetValue(ClassObject, "", null);
                return;
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        /// <summary>
        /// Method: Sets the value for the specified propertyname
        /// </summary>
        /// <param name="propertyName">Name of Property on Class - CASE INSENSITIVE</param>
        /// <param name="value">System.object : Dealts to an ArrayList</param>
        public void SetValue(string propertyName, object value)
        {
            //PropertyInfo pi = null;

            //try
            //{
            //    //TODO: if the specified Property can't hold more than one value -
            //    // Find a better way to hold both values.. append strings?               

            //    pi = ClassPropertyInfo(propertyName);

            //    //If it can't find the property - skip it.
            //    if (pi == null)
            //        return;

            //    pi.SetValue(ClassObject, value, null);
            //}
            //catch
            //{
            //    pi.SetValue(ClassObject, value.ToString().Replace('$', ' '), null);
            //    //SetPropertyValue(propertyName, value.ToString());               
            //}
            try
            {
                PropertyInfo pi = ClassPropertyInfo(propertyName);

                //TODO: Make this dynamic
                if (pi.PropertyType == typeof(System.DateTime))
                {
                    DateTime dt = ConvertDate(value);
                    pi.SetValue(ClassObject, dt, null);
                    return;
                }
                else if (pi.PropertyType == typeof(System.String))
                {
                    pi.SetValue(ClassObject, value.ToString().Replace('$', ' '), null);
                    return;
                }
                else if (pi.PropertyType == typeof(int))
                {
                    pi.SetValue(ClassObject, Convert.ToInt32(value), null);
                    return;
                }

            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        /// <summary>
        /// Method: Return Object Value.
        /// </summary>
        /// <param name="propertyName">The name of the property to get the value for.</param>
        /// <returns>Object</returns>
        public object GetValue(string propertyName)
        {
            string p = ClassProperty(propertyName);
            return ClassPropertyInfo(p).GetValue(ClassObject, null);
        }

        /// <summary>
        /// Returns if the property is read only attribute. 
        /// </summary>
        /// <param name="propertyName">The name of the property to check.</param>
        /// <returns>Bool</returns>
        /// <see cref="http://msdn.microsoft.com/en-us/library/system.componentmodel.readonlyattribute.aspx"/>
        public bool IsReadOnlyProperty(string propertyName)
        {
            try
            {
                PropertyInfo pi = this.ClassPropertyInfo(propertyName);

                AttributeCollection attrs = TypeDescriptor.GetProperties(this)[pi.Name].Attributes;

                ReadOnlyAttribute myAttribute =
                    (ReadOnlyAttribute)attrs[typeof(ReadOnlyAttribute)];

                ReadOnlyAttribute myatt = (ReadOnlyAttribute)attrs[typeof(ReadOnlyAttribute)];

                if (myatt.IsReadOnly)
                    return true;
                else
                    return false;
            }
            catch { return false; }

        }
        #endregion

        #region Private Methods
        private static DateTime ConvertDateString(string date)
        {
            try
            {
                //"2008 05 30 18 12 41 Z";
                int yyyy = Convert.ToInt32(date.Substring(0, 4));

                int mm = Convert.ToInt32(date.Substring(4, 2));

                int dd = Convert.ToInt32(date.Substring(6, 2));

                int hh = Convert.ToInt32(date.Substring(8, 2));

                int min = Convert.ToInt32(date.Substring(10, 2));

                int sec = Convert.ToInt32(date.Substring(12, 2));

                //DateTime dt = new DateTime(yyyy, mm, dd, hh, min, sec); 

                return new DateTime(yyyy, mm, dd, hh, mm, sec);
            }
            catch
            {
                return DateTime.Now.AddDays(-1000);
            }

        }

        /// <summary>
        /// Method: Attempts to convert object to date.
        /// </summary>
        /// <param name="value">The date object.</param>
        /// <returns>DateTime</returns>
        private static DateTime ConvertDate(object value)
        {
            try
            {
                DateTime dt = DateTime.Parse(value.ToString());
                return dt;
            }
            catch
            {
                try
                {
                    DateTime dt = ConvertDateStringLong(value);
                    return dt;

                }
                catch
                {
                    DateTime dt = ConvertDateString(value.ToString());
                    return dt;
                }
            }
        }

        /// <summary>
        /// Method: Converts Ldap value to DateTime.
        /// </summary>
        /// <param name="o">The ldap attribute value.</param>
        /// <returns>DateTime</returns>
        private static DateTime ConvertDateStringLong(object o)
        {
            try
            {
                long longTime = (long)Convert.ToInt64(o);

                DateTime dt = new DateTime(longTime);
                dt = dt.AddYears(1600);

                return dt;

            }
            catch (Exception ex)
            {
                throw ex;
                //return ConvertDateString(o.ToString());
            }

        }
        #endregion

    }
}
